package com.example.adminbase.mybatisgenerator;

import com.example.adminbase.dao.CrudDao;
import com.example.adminbase.service.CrudService;
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.config.PropertyRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;

public class ServiceGenerationPlugin extends PluginAdapter {

    private String targetProject;

    private String targetPackage;

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        targetProject = (String) properties.get("targetProject");
        targetPackage = (String) properties.get("targetPackage");
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        if("true".equals(introspectedTable.getTableConfigurationProperty("generateService"))) {
            return Arrays.asList(
                    serviceInterface(introspectedTable),
                    serviceImpClass(introspectedTable)
            );
        }
        return null;
    }

    private GeneratedJavaFile serviceInterface(IntrospectedTable introspectedTable) {
        Interface service = buildServiceInterface(introspectedTable);

        FullyQualifiedJavaType superInterface = new FullyQualifiedJavaType(CrudService.class.getName());
        if(hasId(introspectedTable)) {
            IntrospectedColumn idColumn = MybatisGeneratorUtil.getIdColumn(introspectedTable);
            FullyQualifiedJavaType pkType = idColumn.getFullyQualifiedJavaType(),
                recordType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
            superInterface.addTypeArgument(pkType);
            superInterface.addTypeArgument(recordType);

            service.addImportedType(pkType);
            service.addImportedType(recordType);
        }
        Set<FullyQualifiedJavaType> superInterfaceTypes = service.getSuperInterfaceTypes();
        superInterfaceTypes.add(superInterface);

        return new GeneratedJavaFile(service,
                targetProject,
                context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                context.getJavaFormatter());
    }

    private boolean hasId(IntrospectedTable introspectedTable) {
        return introspectedTable.getGeneratedKey() != null && introspectedTable.getGeneratedKey().getColumn() != null;
    }

    private Interface buildServiceInterface(IntrospectedTable introspectedTable) {
        Interface service = new Interface(targetPackage + "." +
                introspectedTable.getFullyQualifiedTable().getDomainObjectName() + "Service");
        introspectedTable.setAttribute("service", service);
        CommentGenerator commentGenerator = context.getCommentGenerator();
        service.setVisibility(JavaVisibility.PUBLIC);
        commentGenerator.addJavaFileComment(service);
        return service;
    }

    private GeneratedJavaFile serviceImpClass(IntrospectedTable introspectedTable) {
        Interface interfaze = buildServiceInterface(introspectedTable);
        TopLevelClass serviceImp = new TopLevelClass(targetPackage + ".imp." +
                introspectedTable.getFullyQualifiedTable().getDomainObjectName() + "ServiceImp");
        CommentGenerator commentGenerator = context.getCommentGenerator();
        serviceImp.setVisibility(JavaVisibility.PUBLIC);
        commentGenerator.addJavaFileComment(serviceImp);

        serviceImp.addSuperInterface(interfaze.getType());
        serviceImp.addImportedType(interfaze.getType());
        serviceImp.addImportedType(Service.class.getName());
        serviceImp.addAnnotation("@Service");
        serviceImp.addImportedType(Transactional.class.getName());
        serviceImp.addAnnotation("@Transactional");

        String[] split = introspectedTable.getMyBatis3JavaMapperType().split("\\.");

        String fieldName = StringUtils.uncapitalize(split[split.length - 1]);
        Field field = MybatisGeneratorUtil.buildDeletableField(fieldName,
                new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType()));
        field.setVisibility(JavaVisibility.PRIVATE);
        field.addAnnotation("@Autowired");
        serviceImp.addImportedType(Autowired.class.getName());
        serviceImp.addImportedType(introspectedTable.getMyBatis3JavaMapperType());
        serviceImp.addField(field);

        Method method = MybatisGeneratorUtil.buildDeletableMethod("getDao");
        method.setVisibility(JavaVisibility.PUBLIC);
        method.addAnnotation("@Override");
        method.addBodyLine("return " + fieldName + ";");
        FullyQualifiedJavaType returnType = new FullyQualifiedJavaType(CrudDao.class.getName());
        serviceImp.addImportedType(CrudDao.class.getName());
        returnType.addTypeArgument(MybatisGeneratorUtil.getIdColumn(introspectedTable).getFullyQualifiedJavaType());
        returnType.addTypeArgument(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        serviceImp.addImportedType(introspectedTable.getBaseRecordType());
        method.setReturnType(returnType);
        serviceImp.addMethod(method);

        return new GeneratedJavaFile(serviceImp,
                targetProject,
                context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                context.getJavaFormatter());
    }
}
